#include "app_charger.h"



struct hal_pwm_type PA0_TIM2 = {0}; //pa0 pa1控制充电
struct hal_pwm_type PA11_TIM1 = {0}; //pa11 pa12控制快充

int InputCurrent; //输入电流
int InputVoltage; //输入电压
int BatVoltage;    //电池电压
int BatCurrent;    //电池电流

static struct led_type charge_led = {0};
static uint16_t adc_buf[CONFIG_ADC_NUM];
static PID_Controller pid = {0};
static PID_Controller pid24 = {0};
static uint32_t CURRENT = 0;
static uint32_t VOLTAGE = 0;
static uint8_t  errflag = 0;


void app_charger_adjust(void);
/**
 * @brief 初始化ADC模块。
 * 
 * 配置ADC的引脚、采样个数、缓存及缓存大小，并调用初始化函数。
 * 
 * @note 使用到的ADC引脚包括pa0、pa1、pa5、pa6、pa7。
 */
static void app_adc_init(void)
{
    struct gpio_type adc_pin[CONFIG_ADC_NUM] ={IO_ADC_TEMP,IO_ADC_CURRENT,IO_ADC_BAT,IO_ADC_80V,IO_ADC_24V};   //pa1加入到adc中
    adc1.adc_cnt = CONFIG_ADC_NUM;//ADC采样的个数
    adc1.adc_pin = adc_pin;//使用到的ADC引脚
    adc1.adc_value = adc_buf;//设置缓存
    adc1.adc_value_size = CONFIG_ADC_NUM;//使用到的缓存大小
    adc1.init();    
}
/**
 * @brief 初始化GPIO模块。
 *
 * 配置LED、控制开关、电池电压采集和充电电流控制引脚。
 * 默认状态下，LED关闭，控制开关和充电功能关闭。
 */
static void app_gpio_init(void)
{
    IO_IDC.init(GPIO_Mode_IPU); //快充检测引脚初始化
}

    /**
     * @brief 初始化应用程序的PWM模块和相关硬件
     *
     * 该函数执行以下初始化操作：
     * 1. 配置pa1引脚为推挽输出模式并初始化为低电平
     * 2. 设置TIM1通道1的PWM参数并初始化
     * 3. 初始化PID控制器并设置输出限制
     *
     * @note 这是一个静态函数，仅在当前文件内部使用
     * @note PWM频率固定设置为200kHz
     * @note PID控制器的输出限制设置为20-80
     */
static void app_pwm_init(void)
{
    pa1.init(GPIO_Mode_Out_PP);
    pa1.reset();

    PA0_TIM2.tim = TIM2;
    PA0_TIM2.pin = &pa0;
    PA0_TIM2.frq = 100000;
    PA0_TIM2.channel = (uint8_t)HAL_OC1;

    hal_pwm_init(&PA0_TIM2);   
    hal_pwm_set_duty(&PA0_TIM2,0);   

    PID_Init(&pid,150,50,50);
    PID_SetOutputLimit(&pid, 20, 80);
}
/**
 * @brief 初始化PWM2相关配置
 *
 * 该函数执行以下初始化操作：
 * 1. 配置PA12引脚为推挽输出模式并复位
 * 2. 初始化PA11引脚的TIM1 PWM配置
 * 3. 设置PWM频率为100kHz，占空比为20%
 * 4. 初始化PID控制器参数
 *   - 比例系数: 150
 *   - 积分系数: 50
 *   - 微分系数: 50
 *   - 积分限幅: 0~5
 *   - 输出限幅: 20%~80%
 *   - 目标值: 10000(主值), 100(辅助值)
 *
 * @note 此函数为静态函数，仅在当前文件内可见
 */
static void app_pwm2_init(void)
{
    pa12.init(GPIO_Mode_Out_PP);
    pa12.reset();

    PA11_TIM1.tim = TIM1;
    PA11_TIM1.pin = &pa11;
    PA11_TIM1.frq = 100000;
    PA11_TIM1.channel = (uint8_t)HAL_OC4;

    hal_pwm_init(&PA11_TIM1);   

    PID_Init(&pid24,60,150,50);
    PID_SetTarget(&pid24,10000,300);
}
    /**
     * @brief 处理充电LED状态显示
     *
     * 根据当前充电状态(charge_led.state)控制蓝灯和绿灯的显示:
     * - NO_LOADER: 蓝灯亮，绿灯灭
     * - CHARGING: 蓝灯灭，绿灯闪烁(切换状态)
     * - CHARGE_FULL: 蓝灯灭，绿灯常亮
     * - 其他状态: 双灯灭
     *
     * @note 此函数为静态函数，仅在当前文件内可见
     */
static void app_led_process(void)
{
    switch (charge_led.state)
    {
    case NO_LOADER:
        charge_led.blueled->set();
        charge_led.greenled->reset();
        break;
    case CHARGING:
        charge_led.blueled->reset();
        if(charge_led.greenled->read())
        {
            charge_led.greenled->reset();
        }
        else
        {
            charge_led.greenled->set();
        }
        break;
    case CHARGE_FULL:
        charge_led.blueled->reset();
        charge_led.greenled->set();
        break;
    default:
        charge_led.blueled->reset();
        charge_led.greenled->reset();
        break;
    }
}

    /**
     * @brief 初始化充电指示灯硬件配置
     *
     * 配置蓝色LED(LED2)和绿色LED(LED1)为推挽输出模式并初始化为高电平，
     * 同时初始化定时器2并设置回调函数为app_led_process，启动定时器。
     *
     * @note 该函数会初始化以下硬件资源：
     *       - 蓝色LED: 使用IO_LED2引脚
     *       - 绿色LED: 使用IO_LED1引脚
     *       - 定时器: 使用timer2，周期为3ms
     */
static void app_led_init(void)
{
    charge_led.blueled = &IO_LED2;
    charge_led.blueled->init(GPIO_Mode_Out_PP);
    charge_led.blueled->set();
    charge_led.greenled = &IO_LED1;
    charge_led.greenled->init(GPIO_Mode_Out_PP);
    charge_led.greenled->set();
    charge_led.tim = &timer3;
    charge_led.tim->callback = app_led_process;
    charge_led.tim->init(3);
    charge_led.tim->start();
}
static void app_led_ctl(uint8_t state)
{
    charge_led.state = state;
}
/**
 * @brief 转换ADC采样值为电压值（单位：mV）。
 *
 * 将ADC采样值转换为实际的电压值（毫伏）。
 *
 * @param adc_num ADC通道号。
 * @return int 转换后的电压值（单位：mV）。
 *  */
static int app_transform_adc(int adc_num)
{
    int ret = 0;
    ret = (adc_buf[adc_num] * 3300)/4096;
    switch(adc_num)
    {
        case ADC_TEMP:
            break;
        case ADC_CURRENT:            
            ret = ret * 1000 / 20 / 51;  //mA为单位
            break;
        case ADC_BAT:
            ret = ret * 206 / 10;
            break;
        case ADC_80V:
            ret = ret * 1033 / 33;
            break;
        case ADC_24V:
            ret = ret * 1100 / 100;
            break;
    }
    return ret;
}


/**
 * @brief 设置目标电流值
 *
 * 通过 PID 控制器设置目标电流值，并指定 100mA 的死区范围。
 *
 * @param current 要设置的目标电流值（单位：mA）
 */
static void app_set_current(int current)
{
    PID_SetTarget(&pid, current, 50); // 死区50MA
}

static void app_get_config(void)
{
    hal_flash_read(CURRENT_ADDRESS,(uint8_t *)(&CURRENT),sizeof(CURRENT));
    hal_flash_read(VOLTAGE_ADDRESS,(uint8_t *)(&VOLTAGE),sizeof(VOLTAGE));
    printf("********current=%dmA voltage=%dmV*********\r\n",CURRENT,VOLTAGE);
    if(VOLTAGE > 55000) //最大充电电压55v
    {
        errflag |= CONFIG_V_ERR;
    }
    if(CURRENT > 4000) //最大充电电流4A
    {
        errflag |= CONFIG_I_ERR;   
    }
}




/**
 * @brief 将字符串中的数字部分转换为整数
 *
 * 该函数从给定的字节数组中提取连续的数字字符，并将其转换为对应的整数值。
 * 函数会跳过前导非数字字符，直到找到第一个数字字符开始转换。
 *
 * @param data 指向包含数字字符的字节数组的指针
 * @return int 转换得到的整数值
 * @note 函数不会检查数组越界，调用者需确保数组有足够的长度
 */
int tonum(uint8_t *data)
{
    int i = 0;
    int num =0;
    while(data[i] < '0' || data[i] > '9')
    {
        i++;
    }
    while(data[i] >= '0' && data[i] <= '9')
    {
        num *= 10;
        num += data[i] - '0';
        i++;
    }
    return num;
}
void app_check_loader(void)
{
    pa1.reset();
    hal_pwm_set_duty(&PA0_TIM2,0);//关闭pwm的充电
    delay_ms(10);
    if(app_transform_adc(ADC_BAT) < 10000) //电池电压过低，可能没有电池，禁止充电
    {
        app_led_ctl(NO_LOADER);
    }
}

/**
 * @brief 充电器调节函数
 *
 * 该函数通过ADC采样当前电流值，使用PID位置式算法计算PWM占空比，
 * 并将计算结果应用于PWM输出以调节充电器工作状态。
 *
 * @note 函数内部会自动完成ADC采样、PID计算和PWM设置
 * @warning 调用此函数前需确保PID控制器已正确初始化
 */
void app_charger_adjust(void)
{
    int current = 0;
    int voltage = 0;
    static int pwm = 0;
    int add_pwm = 0;
    if(errflag)  //检测到有错误就停止充电，关闭pwm   
    {
        pa1.reset();
        hal_pwm_set_duty(&PA0_TIM2,0);
        return;
    }
    current = app_transform_adc(ADC_CURRENT);
    voltage = app_transform_adc(ADC_BAT);

    add_pwm = PID_Incremental_Calculate(&pid,current);
    if(add_pwm > 50) add_pwm = 50;
    if(add_pwm < -50) add_pwm = -50;
    pwm += add_pwm;
    if(pwm < 2000) pwm = 2000;
    if(pwm > 8000) pwm = 8000;

    if(voltage > VOLTAGE) pwm = pwm / 2;
    
    pa1.set();
    hal_pwm_set_duty(&PA0_TIM2,pwm/100);
}
void app_24v_adjust(void)
{
    int voltage = 0;  
    static int pwm2 = 0;
    int add_pwm = 0;      
    voltage = app_transform_adc(ADC_24V);
    add_pwm = PID_Incremental_Calculate(&pid24,voltage);    
    if(add_pwm > 500) add_pwm = 500;
    if(add_pwm < -500) add_pwm = -500;
    pwm2 += add_pwm;
    if(pwm2 < 0) pwm2 = 0; 
    if(pwm2 > 7000) pwm2 = 7000;  
    pa12.set();
    hal_pwm_set_duty(&PA11_TIM1,pwm2/100);     
    printf("%d,%d,%d\r\n",add_pwm,voltage,pwm2); 
}
/**
 * @brief 充电器模块的初始化函数。
 *
 * 调用ADC和GPIO的初始化函数，完成充电器模块的初始化。
 */
void app_charger_init(void)
{
    app_adc_init(); // 初始化ADC模块
    app_gpio_init(); // 初始化GPIO模块
    app_get_config();//获取配置信息
    app_pwm_init();//pwm初始化
    app_pwm2_init();//pwm初始化
    app_led_init();
}
/**
 * @brief 充电控制主循环函数
 *
 * 该函数根据当前电池电压值，自动调整充电电流或停止充电：
 * 1. 当电压低于目标电压60%时，采用涓流充电（1/10额定电流）
 * 2. 当电压在目标电压60%-90%之间时，采用正常充电（额定电流）
 * 3. 当电压在目标电压90%-100%之间时，再次采用涓流充电
 * 4. 当电压达到或超过目标电压时，完全停止充电
 *
 * @note 函数通过ADC_BAT通道读取电池电压，使用app_transform_adc()进行转换
 * @note 充电控制通过app_set_current()和hal_pwm_set_duty()实现
 */
void app_charger_loop(void)
{
    int cur_voltage = 0; // 当前电压
    // int cur_temp = 0;    // 当前温度 
    int cur_current = 0; //当前电流
    int cur_80v = 0;     //当前输入电压
    int cur_24v = 0;     //当前降压电压
    cur_current = app_transform_adc(ADC_CURRENT);
    cur_voltage = app_transform_adc(ADC_BAT);
    // cur_temp = app_transform_adc(ADC_TEMP);
    cur_80v = app_transform_adc(ADC_80V); 
    cur_24v = app_transform_adc(ADC_24V);
    // printf("current=%d voltage=%d input=%d output=%d\r\n",cur_current,cur_voltage,cur_80v,cur_24v);
    // if(cur_temp < ALARM_TEMP) //温度过高，停止充电
    // {
    //     errflag |= TEMP_ERR;
    // }
    // if(errflag & TEMP_ERR) //温度恢复正常，清除错误标志
    // {
    //     if(cur_temp > ALARM_TEMP) //恢复到安全温度以下
    //     {
    //         errflag &= ~TEMP_ERR;
    //     }
    // }
    if(errflag)
    {
        if(errflag & TEMP_ERR)
            printf("TEMP_ERR\r\n");
        if(errflag & CONFIG_I_ERR)
            printf("CONFIG_I_ERR\r\n");
        if(errflag & CONFIG_V_ERR)
            printf("CONFIG_V_ERR\r\n");
        return;
    }
    app_set_current(CURRENT);
    // if(IO_IDC.read() == 0) //检测到快充，降低充电电流
    // {
    //     app_set_current(CURRENT / 10);
    //     app_led_ctl(CHARGING);
    // }
    // else if(cur_voltage < VOLTAGE * 6 / 10)  //电压过低，涓流充电
    // {
    //     app_set_current(CURRENT);
    //     app_led_ctl(CHARGING);        
    // }
    // else if(cur_voltage < VOLTAGE * 9 / 10) //正常充电
    // {
    //     app_set_current(CURRENT);
    //     app_led_ctl(CHARGING);        
    // }
    // else if(cur_voltage < VOLTAGE)  //接近目标电压，涓流充电
    // {
    //     app_set_current(CURRENT);
    //     app_led_ctl(CHARGING);        
    // }
    // else  //电压过高，关闭pwm,停止充电
    // {
    //     hal_pwm_set_duty(&PA0_TIM2,0);
    //     app_check_loader();
    // }
}